package com.d2rv.user;

import com.d2rv.guide.Goal;
import com.d2rv.guide.Instruction;
import com.d2rv.model.ArrowModel;
import com.d2rv.model.UserModel;
import com.d2rv.offscreen.OffScreen3D;
import com.d2rv.settings.Settings;
import processing.core.PApplet;
import processing.core.PVector;

/**
 * This class creates a user with a position, a headposition, a defualt speed, a
 * target to follow and an angle of rotation.
 *
 * @author Darrieutort A., Dupouy A., Rossignol L., Van Poucke A.
 */
public class User {

    private PVector headposition;
    private PVector position;
    private PVector speed;
    private PVector target;
    private float rotate;

    /**
     * Default constructor.
     *
     * @param parent the current application
     * @param position the initial position of the user
     * @param speed the speed
     * @param target the initial target
     */
    public User(PApplet parent, PVector position, PVector speed, PVector target) {
        this.position = position;
        this.headposition = new PVector(position.x, position.y, position.z);
        this.speed = speed;
        this.target = target;
        this.rotate = 0.0f;
    }

    /**
     * Returns the position of the user.
     *
     * @return the position of the user
     */
    public PVector position() {
        return this.position;
    }

    /**
     * Updates the position of the user.
     *
     * @param position the new position of the user
     */
    public void position(PVector position) {
        this.position = position;
    }

    /**
     * Returns the user head position.
     *
     * @return the user head position
     */
    public PVector headposition() {
        return this.headposition;
    }

    /**
     * Updates the user head position.
     *
     * @param p the new position of the head
     */
    public void headposition(PVector p) {
        this.headposition.x = p.x;
        this.headposition.y = p.y;
        this.headposition.z = p.z;
    }

    /**
     * Returns the speed of the user.
     *
     * @return the speed of the user
     */
    public PVector speed() {
        return this.speed;
    }

    /**
     * Updates the speed of the user.
     *
     * @param speed the new speed
     */
    public void speed(PVector speed) {
        this.speed = speed;
    }

    /**
     * Return the target of the user.
     *
     * @return the target of the user
     */
    public PVector target() {
        return this.target;
    }

    /**
     * Updates the target of the user.
     *
     * @param target the new target of the user
     */
    public void target(PVector target) {
        this.target = target;
    }

    /**
     * Returns the rotation of the user.
     *
     * @return the rotation of the user
     */
    public float rotate() {
        return this.rotate;
    }

    /**
     * Updates the rotation of the user.
     *
     * @param rotate the new angle of rotation
     */
    public void rotate(float rotate) {
        this.rotate = rotate;
    }

    /**
     * Draws the user on the user guide map.
     *
     * @param parent the current offscreen
     * @param usermodel the user model to draw
     */
    public void draw(OffScreen3D parent, UserModel usermodel) {
        Settings settings = Settings.getInstance();

        float angle = (float) Math.atan((settings.screenwidth() / 2f) / -this.headposition.x);
        float far = 10000f / (float) Math.cos(angle);

        parent.pushMatrix();
        parent.translate(this.position.x, this.position.y, this.position.z);
        parent.rotateY(this.rotate);

        usermodel.draw(parent);

        /* FOV user */
        parent.pushMatrix();
        parent.translate(-this.headposition.x, 0, 0);
        parent.box(0.1f, settings.screenheight(), settings.screenwidth());
        parent.popMatrix();

        parent.pushMatrix();
        parent.rotateY(angle);
        parent.translate(far / 2, 0, 0);
        parent.noStroke();
        parent.box(far, 0.1f, 0.5f);
        parent.popMatrix();
        parent.pushMatrix();
        parent.rotateY(-angle);
        parent.translate(far / 2, 0, 0);
        parent.noStroke();
        parent.box(far, 0.1f, 0.5f);
        parent.popMatrix();

        parent.popMatrix();
    }

    /**
     * Draws instruction in front of the user.
     *
     * @param parent the current offscreen
     * @param instruction
     */
    public void drawInstruction(OffScreen3D parent, Instruction instruction) {
        if (instruction != null) {
            parent.pushMatrix();
            parent.translate(this.position.x, this.headposition.y, this.position.z);
            parent.rotateY(this.rotate);
            instruction.draw(parent);
            parent.popMatrix();
        }
    }

    /**
     * Draws a arrow oriented towards the current destination in front of the
     * user.
     *
     * @param parent the curent offscreen
     * @param goal the current goal
     * @param arrowmodel the arrow model
     */
    public void drawArrow(OffScreen3D parent, Goal goal, ArrowModel arrowmodel) {
        if (goal != null) {
            if (goal.position() != null) {

                PVector v1 = new PVector(target.x - this.position.x,
                        0,
                        target.z - this.position.z);

                PVector v2 = new PVector(goal.position().x - this.position.x,
                        0,
                        goal.position().z - this.position.z);

                float angleTarget = PVector.angleBetween(v2, v1);

                PVector normal = v1.cross(v2);

                if (normal.y <= 0) {
                    angleTarget = -angleTarget;
                }

                parent.pushMatrix();

                parent.translate(this.position.x, this.headposition.y, this.position.z);
                parent.rotateY(this.rotate);

                arrowmodel.draw(parent, angleTarget);

                parent.popMatrix();
            }
        }
    }
}